package com.pig4cloud.pig.common.swagger.plugins;

import cn.hutool.core.lang.Pair;
import com.bblocks.config.SwaggerDisplayEnum;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.pig4cloud.pig.common.swagger.support.EnumPluginUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import springfox.documentation.builders.ModelSpecificationBuilder;
import springfox.documentation.builders.PropertySpecificationBuilder;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>Project: bblocks-pro - EnumModelPropertyBuilderPlugin</p>
 * <p>描述：枚举支持（json）
 * 参考文档：https://blog.icexmoon.cn/archives/451.html
 * </p>
 *
 * @Author Zhao [125043150@qq.com]
 * @Date 2024/3/1 16:36
 * @Version 1.0
 * @since 8
 */
@Order
@Component
public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
    @Override
    public void apply(ModelPropertyContext context) {
        Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
        if (!optional.isPresent()) {
            return;
        }

        final Class<?> fieldType = optional.get().getField().getRawType();
        addDescForEnum(context, fieldType);
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }

    private void addDescForEnum(ModelPropertyContext context, Class<?> fieldType) {
        if (Enum.class.isAssignableFrom(fieldType)) {
            SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(fieldType, SwaggerDisplayEnum.class);
            if (annotation != null) {
                Object[] enumConstants = fieldType.getEnumConstants();
                List<Pair<Object,String>> enumKeyValues = EnumPluginUtils.getEnumKeyValues(enumConstants, annotation);
                List<String> displayValues = enumKeyValues
                        .stream()
                        .map(tuple -> tuple.getKey() + "-" + tuple.getValue())
                        .collect(Collectors.toList());
                List<String> availableValues = enumKeyValues.stream().map(tuple -> tuple.getKey().toString()).collect(Collectors.toList());
                PropertySpecificationBuilder specificationBuilder = context.getSpecificationBuilder();
                Field descField = ReflectionUtils.findField(specificationBuilder.getClass(), "description");
                descField.setAccessible(true);
                String joinText = null;
                try {
                    joinText = (String) descField.get(specificationBuilder);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                joinText = joinText + "(" + String.join("; ", displayValues) + ")";

                Pair<ScalarType,String> valueType = EnumPluginUtils.getValueType(enumKeyValues.get(0).getKey());
                specificationBuilder.type(new ModelSpecificationBuilder().scalarModel(valueType.getKey()).build());
                specificationBuilder.description(joinText);
                AllowableListValues allowableListValues = new AllowableListValues(availableValues, valueType.getValue());
                specificationBuilder.enumerationFacet(builder -> builder.allowedValues(allowableListValues));
            }
        }
    }
}

